;__VDUCONS_________________________________________________________________________________________
;
;	VDUCONS
;
;	VDU DRIVERS BY:  ANDREW LYNCH
;	KEYBOARD DRIVERS BY: DR. JAMES MOXHAM
;	REMAINDER WRITTEN BY: DAN WERNER -- 11/7/2009
; 	CONVERTED TO 6809:  Dan Werner 6/27/2010
;__________________________________________________________________________________________________
;

; DATA CONSTANTS
;__________________________________________________________________________________________________
;IDE REGISTER		IO PORT		; FUNCTION
READR		.EQU	$F0		; READ VDU
WRITR		.EQU	$F1		; WRITE VDU
SY6545S		.EQU	$F2		; VDU STATUS/REGISTER
SY6545D		.EQU	$F3		;
ppia		.EQU	$F4		; PPI port A
ppib		.EQU	$F5		; PPI port B
ppic		.EQU	$F6		; PPI port C
ppicont		.EQU	$F7		; PPI control port
MPCL_RAM:	.EQU 	78H		; BASE IO ADDRESS OF RAM MEMORY PAGER CONFIGURATION LATCH
MPCL_ROM:	.EQU 	7CH		; BASE IO ADDRESS OF ROM MEMORY PAGER CONFIGURATION LATCH

STATE_NORMAL	.EQU	00H		; NORMAL TERMINAL OPS
STATE_ESC	.EQU	01H		; ESC MODE
STATE_DIR_L	.EQU	02H		; ESC-Y x *
STATE_DIR_C	.EQU	03H		; ESC-Y * x

ESC_KEY		.EQU	1BH		; ESCAPE CODE




	.ORG	$0100
;__________________________________________________________________________________________________
; RELOCATE PROGRAM BEGINS HERE
;__________________________________________________________________________________________________
	DI				; DISABLE INTERRUPTS
	LD	BC,2000H		; BYTES TO MOVE
	LD	DE,8000H		; DESTINATION ADDRESS
	LD	HL,0100H		; SOURCE ADDRESS
	LDIR				; COPY RAM
	JP	800FH			; JUMP TO COPIED IMAGE
	; THIS CODE IS RAN IN RAM, 8000H
	LD	A,99H			; CHOOSE HIGHEST RAM PAGE
	OUT	(MPCL_RAM),A		; SEND 19H TO RAM MAP PORT (SELECT HIGHEST RAM PAGE)
	LD	BC,2000H		; BYTES TO MOVE
	LD	DE,0100H		; DESTINATION ADDRESS
	LD	HL,8000H		; SOURCE ADDRESS
	LDIR				; COPY RAM
	JP	0121H			; JUMP TO COPIED IMAGE
	; BACK TO 0121H, BUT IN PAGE 19H
	CALL	INITVDU			; INIT THE VDU CARD

	LD	BC,ENDSTUBS-BEGINSTUBS	; BYTES TO MOVE
	LD	DE,0FF00H		; DESTINATION ADDRESS
	LD	HL,BEGINSTUBS		; SOURCE ADDRESS
	LDIR				; COPY RAM

	LD	A,80H			; BACK TO NORMAL OPERATING PAGE	
	OUT	(MPCL_RAM),A		; SEND 19H TO RAM MAP PORT (SELECT HIGHEST RAM PAGE)
	
	LD	HL,(0001H)		; GET WBOOT ADDRESS
	INC	HL			; FORWARD TO CONST ADDRESS
	INC	HL			;
	INC	HL			;
	INC	HL			;

	LD	DE,NEWCONST-BEGINSTUBS+0FF00H
	LD	(HL),E			; PATCH CP/M CONST
	INC 	HL			;
	LD	(HL),D			; 
	INC	HL			; FORWARD TO CONIN ADDRESS
	INC	HL			;
	LD	DE,NEWCONIN-BEGINSTUBS+0FF00H
	LD	(HL),E			; 
	INC 	HL			;
	LD	(HL),D			; 
	INC	HL			; FORWARD TO CONOUT ADDRESS
	INC	HL			;
	LD	DE,NEWCONOUT-BEGINSTUBS+0FF00H
	LD	(HL),E			; 
	INC 	HL			;
	LD	(HL),D			; 
	INC	HL			; FORWARD TO LIST ADDRESS
	INC	HL			;
	LD	DE,NEWLIST-BEGINSTUBS+0FF00H
	LD	(HL),E			; 
	INC 	HL			;
	LD	(HL),D			; 

	
	
	LD	C,00H			; CP/M SYSTEM RESET CALL
	CALL	0005H			; RETURN TO PROMPT


	

;__________________________________________________________________________________________________
; STUB CODE, THIS GETS COPIED TO FF00 AND RAN FROM CP/M TO SWITCH CONTEXT
;__________________________________________________________________________________________________

BEGINSTUBS:

NEWCONST:
	DI				; DISABLE INTERRUPTS	
	LD	A,80H			; LOAD VALUE TO SWITCH OUT ROM
 	OUT	(MPCL_ROM),A		;
	LD	A,99H			; CHOOSE HIGHEST RAM PAGE
	OUT	(MPCL_RAM),A		; SEND 19H TO RAM MAP PORT (SELECT HIGHEST RAM PAGE)
	LD	HL,0			;
	ADD	HL,SP			; GET STACK POINTER INTO HL
	LD	(PARKSTACK),HL		; SAVE STACK POINTER 
	LD	SP,TERMSTACK		; 
	CALL	IS_KBHIT		; CHECK FOR KB HIT
	LD	C,A			; STORE RESULT
	LD	SP,(PARKSTACK)		; RESTORE STACK
	LD	A,80H			; LOAD VALUE TO SWITCH OUT ROM
 	OUT	(MPCL_ROM),A		;
	LD	A,00H			; CHOOSE HIGHEST RAM PAGE
	OUT	(MPCL_RAM),A		; SEND 19H TO RAM MAP PORT (SELECT HIGHEST RAM PAGE)
	LD	A,C			; RESTORE RESULT
	EI				; ENABLE INTERRUPTS
	RET


NEWCONIN:
	DI				; DISABLE INTERRUPTS	
	LD	A,80H			; LOAD VALUE TO SWITCH OUT ROM
 	OUT	(MPCL_ROM),A		;
	LD	A,99H			; CHOOSE HIGHEST RAM PAGE
	OUT	(MPCL_RAM),A		; SEND 19H TO RAM MAP PORT (SELECT HIGHEST RAM PAGE)
	LD	HL,0			;
	ADD	HL,SP			; GET STACK POINTER INTO HL
	LD	(PARKSTACK),HL		; SAVE STACK POINTER 
	LD	SP,TERMSTACK		; 
	CALL	GET_KEY			; GET KEY FROM KEYBOARD	
	LD	C,A			; STORE RESULT
	LD	SP,(PARKSTACK)		; RESTORE STACK
	LD	A,80H			; LOAD VALUE TO SWITCH OUT ROM
 	OUT	(MPCL_ROM),A		;
	LD	A,00H			; CHOOSE HIGHEST RAM PAGE
	OUT	(MPCL_RAM),A		; SEND 19H TO RAM MAP PORT (SELECT HIGHEST RAM PAGE)
	LD	A,C			; RESTORE RESULT
	EI				; ENABLE INTERRUPTS
	RET
	
NEWCONOUT:
	DI				; DISABLE INTERRUPTS	
	LD	A,80H			; LOAD VALUE TO SWITCH OUT ROM
 	OUT	(MPCL_ROM),A		;
	LD	A,99H			; CHOOSE HIGHEST RAM PAGE
	OUT	(MPCL_RAM),A		; SEND 19H TO RAM MAP PORT (SELECT HIGHEST RAM PAGE)
	LD	HL,0			;
	ADD	HL,SP			; GET STACK POINTER INTO HL
	LD	(PARKSTACK),HL		; SAVE STACK POINTER 
	LD	SP,TERMSTACK		; 
	LD	A,C			; RESTORE CHARACTER
	CALL	CHARIN			; DISPLAY CHARACTER	
	LD	SP,(PARKSTACK)		; RESTORE STACK
	LD	A,80H			; LOAD VALUE TO SWITCH OUT ROM
 	OUT	(MPCL_ROM),A		;
	LD	A,00H			; CHOOSE HIGHEST RAM PAGE
	OUT	(MPCL_RAM),A		; SEND 19H TO RAM MAP PORT (SELECT HIGHEST RAM PAGE)
	EI				; ENABLE INTERRUPTS
	RET
NEWLIST:
	DI				; DISABLE INTERRUPTS	
	LD	A,80H			; LOAD VALUE TO SWITCH OUT ROM
 	OUT	(MPCL_ROM),A		;
	LD	A,99H			; CHOOSE HIGHEST RAM PAGE
	OUT	(MPCL_RAM),A		; SEND 19H TO RAM MAP PORT (SELECT HIGHEST RAM PAGE)
	LD	HL,0			;
	ADD	HL,SP			; GET STACK POINTER INTO HL
	LD	(PARKSTACK),HL		; SAVE STACK POINTER 
	LD	SP,TERMSTACK		; 
	LD	A,C			; RESTORE CHARACTER
    	CALL    PR_OUTCHAR		;
	LD	SP,(PARKSTACK)		; RESTORE STACK
	LD	A,80H			; LOAD VALUE TO SWITCH OUT ROM
 	OUT	(MPCL_ROM),A		;
	LD	A,00H			; CHOOSE HIGHEST RAM PAGE
	OUT	(MPCL_RAM),A		; SEND 19H TO RAM MAP PORT (SELECT HIGHEST RAM PAGE)
	EI				; ENABLE INTERRUPTS
	RET
ENDSTUBS:	

		

		
	

	

;__________________________________________________________________________________________________
; MAIN PROGRAM BEGINS HERE
;__________________________________________________________________________________________________
INITVDU:
    	CALL	VDU_INIT		; INIT VDU   					
	CALL 	KB_INITIALIZE		; INIT KB
	CALL	PR_INITIALIZE		; INIT PR
    	
    	CALL	DSPMATRIX		; DISPLAY INIT MATRIX SCREEN
	CALL	WAIT_KBHIT		; WAIT FOR A KEYSTROKE
	LD	A,0			; EMPTY KB QUEUE
	LD	(KB_QUEUE_PTR),A	; 
	
	CALL	PERF_ERASE_EOS		; CLEAR SCREEN
	CALL	PERF_CURSOR_HOME	; CURSOR HOME	
	RET


;__CHARIN__________________________________________________________________________________________
;
; 	PROCESS INCOMMING CHARACTER AND DISPLAY ON SCREEN OR PERFORM FUNCTION
;	A:  INCOMMING CHARACTER
;__________________________________________________________________________________________________
CHARIN:
	PUSH	AF			; STORE AF
	LD	A,(TERMSTATE)		; MOVE CURRENT STATE INTO A
	CP	STATE_NORMAL		; NORMAL PROCESSING STATE?
	JP	Z,CHARIN_NORM		;
	CP	STATE_ESC		; ESCAPE PROCESSING STATE?
	JP	Z,CHARIN_ESCSTATE	;
	CP	STATE_DIR_L		; WAITING FOR Y COORD STATE?
	JP	Z,CHARIN_DIR_L_STATE	;
	CP	STATE_DIR_C		; WAITING FOR X COORD STATE?
	JP	Z,CHARIN_DIR_C_STATE	;
	LD	A,STATE_NORMAL		; UNKNOWN STATE, RESET STATE
	LD	(TERMSTATE),A		;
	POP	AF			; 
	RET				;

;__CHARIN_DIR_L_STATE______________________________________________________________________________
;
; 	PROCESS "WAITING FOR Y COORD STATE"
;__________________________________________________________________________________________________
CHARIN_DIR_L_STATE:
	LD	A,32			; MOVE 32 (' ') INTO A
	LD	C,A			; PARK INTO C
	POP	AF			; GET CHAR FROM STACK
	SUB	C			; DECODE CHAR INTO USABLE Y COORD
	CP	24			; IS OFF SCREEN?
	JP	M,CHARIN_DIR_L_STATE_CONT
	LD	A,23			; YES, PLACE CRSR ON LAST ROW
CHARIN_DIR_L_STATE_CONT:	
	LD	(TERM_Y),A		; NO, USE DECODED VALUE	
	LD	A,STATE_DIR_C		; SET UP STATE TO GET X COORD
	LD	(TERMSTATE),A		;
	RET

;__CHARIN_DIR_C_STATE______________________________________________________________________________
;
; 	PROCESS "WAITING FOR X COORD STATE"
;__________________________________________________________________________________________________	
CHARIN_DIR_C_STATE:
	LD	A,32			; MOVE 32 (' ') INTO A
	LD	C,A			; PARK INTO C
	POP	AF			; GET CHAR FROM STACK
	SUB	C			; DECODE CHAR INTO USABLE X COORD
	CP	80			; IS OFF SCREEN?
	JP	M,CHARIN_DIR_C_STATE_CONT
	LD	A,79			; YES, PLACE CRSR IN LAST COLUMN
CHARIN_DIR_C_STATE_CONT:	
	LD	(TERM_X),A		; NO, USE DECODED VALUE
	CALL 	GOTO_XY			; SET CURSOR POS
	LD	A,STATE_NORMAL		; RESET STATE TO NORMAL
	LD	(TERMSTATE),A		;
	RET
	
	
;__CHARIN_NORM_____________________________________________________________________________________
;
; 	PROCESS NORMAL STATE
;__________________________________________________________________________________________________	
CHARIN_NORM:
	POP	AF			; GET CHAR FROM STACK
	CP	0AH			; IS LINEFEED?
	JP	Z,CHARIN_LF		;
	CP	09H			; IS TAB?
	JP	Z,CHARIN_TAB		;
	CP	08H			; IS BS?
	JP	Z,CHARIN_BS		;
	CP	0DH			; IS CR?
	JP	Z,CHARIN_CR		;
	CP	07H			; IS BELL?
	JP	Z,CHARIN_BELL		;
	CP	13H			; IS XOFF?
	JP	Z,CHARIN_XOFF		;
	CP	11H			; IS XON?
	JP	Z,CHARIN_XON		;
	CP	ESC_KEY			; IS ESC?
	JP	Z,CHARIN_ESC		;
	JP	VDU_PutChar		; NORMAL OUTPUT CHAR
	
;__CHARIN_ESC______________________________________________________________________________________
;
; 	PROCESS "ESC" STATE
;__________________________________________________________________________________________________	
CHARIN_ESC:	
	LD	A,STATE_ESC		; ESC PRESSED, STATE TO ESCPRESSED
	LD	(TERMSTATE),A		;
    	RET
    	
;__CHARIN_LF_______________________________________________________________________________________
;
; 	PROCESS LINE FEED
;__________________________________________________________________________________________________	
CHARIN_LF:
	LD	A,(TERM_Y)		; MOVE CRSR Y COORD INTO A
	INC	A			; INC A
	LD	(TERM_Y),A		; STORE NEW Y COORD 
	JP	GOTO_XY			; SET CRSR POSITION
	
	
;__CHARIN_TAB______________________________________________________________________________________
;
; 	PROCESS TABS
;__________________________________________________________________________________________________	
CHARIN_TAB:
	LD	HL,TABSTOPS		; SET HL TO TAB STOP TABLE
	LD	A,(TERM_X)		; MOVE CURENT CRSR X COORD INTO A
	INC	A			; INC A
	LD	C,A			; STORE CRSR X COORD INTO A
CHARIN_TAB_LOOP:	
	LD	A,(HL)			; GET NEXT TAB STOP
	OR	A			; IS ZERO?
	JP	Z,CHARIN_TAB_EXIT	; END OF TABLE, PROCESS 73+
	INC	HL			; SET POINTER TO NEXT TABLE ENTRY
	CP	C			; IS CURRENT ENTRY > X COORD?
	JP	M,CHARIN_TAB_LOOP	; NO, LOOP
	LD	(TERM_X),A		; YES, USE IT
	JP	GOTO_XY			; SET CRSR POSITION
CHARIN_TAB_EXIT:	
	LD	A,(TERM_X)		; COLUMN IS PAST LAST TAB STOP, SET A TO CRSR POS
	CP	79			; IS LAST PHYSICAL POS?	
	RET	Z			; YES, DO NOTHING
	INC	A			; NO, INC CRSR BY ONE
	LD	(TERM_X),A		; STORE NEW X COORD
	JP	GOTO_XY			; SET CRSR POSITION
	
TABSTOPS:	
	.DB	09,17,25,33,41,49,57,65,73,00
	
		
;__CHARIN_BS_______________________________________________________________________________________
;
; 	PROCESS BACKSPACE
;__________________________________________________________________________________________________	
CHARIN_BS:		
	JP	PERF_CURSOR_LEFT	; PERFORM CRSR LEFT FUNCTION
	
	
;__CHARIN_CR_______________________________________________________________________________________
;
; 	PROCESS CARRAGE RETURN
;__________________________________________________________________________________________________	
CHARIN_CR:
	LD	A,00H			; MOVE 0 TO X COORD 
	LD	(TERM_X),A		;
	JP	GOTO_XY			; GOTO XY COORDS
	
	
;__CHARIN_BELL_____________________________________________________________________________________
;
; 	PROCESS BELL 
;__________________________________________________________________________________________________	
CHARIN_BELL:
	;
	; NO HARDWARE FOR THIS, DO NOTHING
	;
	RET
	
	
;__CHARIN_XOFF_____________________________________________________________________________________
;
; 	PROCESS XOFF 
;__________________________________________________________________________________________________	
CHARIN_XOFF:
	;
	; SHOULD NOT BE NECESSARY FOR LOCAL IMPLIMENTATION
	;
	RET	

	
;__CHARIN_XON______________________________________________________________________________________
;
; 	PROCESS XON 
;__________________________________________________________________________________________________	
CHARIN_XON:
	;
	; SHOULD NOT BE NECESSARY FOR LOCAL IMPLIMENTATION
	;
	RET	
	
	
;__CHARIN_ESCSTATE_________________________________________________________________________________
;
; 	PROCESS ESC STATE 
;__________________________________________________________________________________________________	
CHARIN_ESCSTATE:
	POP	AF			;
	CP	'A'			; IS CURSOR UP?
	JP	Z, PERF_CURSOR_UP	;
	CP	'B'			; IS CURSOR DOWN?
	JP	Z, PERF_CURSOR_DOWN	;
	CP	'C'			; IS CURSOR RIGHT?
	JP	Z, PERF_CURSOR_RIGHT	;
	CP	'D'			; IS CURSOR LEFT?
	JP	Z, PERF_CURSOR_LEFT	;
	CP	'F'			; IS ENTER GRAPHICS MODE?
	JP	Z, PERF_ENTER_GR	;
	CP	'G'			; IS EXIT GRAPHICS MODE?
	JP	Z, PERF_EXIT_GR		;
	CP	'H'			; IS CURSOR HOME?
	JP	Z, PERF_CURSOR_HOME	;
	CP	'I'			; IS CURSOR HOME?
	JP	Z, PERF_REVERSE_LF	;
	CP	'Y'			; IS REVERSE LINE FEED?
	JP	Z, PERF_DIRECT_ADDRESS	;
	CP	'K'			; IS ERASE TO END OF LINE?
	JP	Z,PERF_ERASE_EOL	;
	CP	'J'			; IS ERASE TO END OF SCREEN?
	JP	Z,PERF_ERASE_EOS	;	
	CP	'Z'			; IS TERMINAL IDENTIFY?
	JP	Z,PERF_IDENTIFY		;	
	CP	'{'			; IS ENTER HOLD SCREEN MODE?
	JP	Z,PERF_ENTER_HOLD	;	
	CP	05CH			; IS EXIT HOLD SCREEN MODE?
	JP	Z,PERF_EXIT_HOLD	;	
	CP	'='			; IS ENTER ALT KEYPAD MODE?
	JP	Z,PERF_ENTER_ALT	;	
	CP	'}'			; IS EXIT ALT KEYPAD MODE?
	JP	Z,PERF_EXIT_ALT		;	
	CALL	VDU_PutChar		; NORMAL OUTPUT CHAR
	JP	SET_STATE_NORMAL	;


;__PERF_REVERSE_LF_________________________________________________________________________________
;
; 	PERFORM REVERSE LINE FEED
;__________________________________________________________________________________________________	
PERF_REVERSE_LF:
	CALL	SET_STATE_NORMAL	; SET STATE TO NORMAL
	LD	A,(TERM_Y)		; GET CURRENT Y COORD INTO A
	OR	A			; IS ZERO
	JP	Z,REVERSE_SCROLL	; YES, SCROLL SCREEN DOWN ONE LINE
	DEC	A			; NO, MOVE CRSR UP ONE LINE
	LD	(TERM_Y),A		; STORE NEW CRSR POSITION
	JP	GOTO_XY			; POSITION CRSR
	
	
;__PERF_DIRECT_ADDRESS_____________________________________________________________________________
;
; 	PERFORM DIRECT CURSOR ADDRESSING
;__________________________________________________________________________________________________	
PERF_DIRECT_ADDRESS:	
	LD	A,STATE_DIR_L		; SET STATE "WAITING FOR Y COORD"
	LD	(TERMSTATE),A		;
    	RET				;
    	
;__PERF_ENTER_GR___________________________________________________________________________________
;
; 	PERFORM ENTER GRAPHICS MODE
;__________________________________________________________________________________________________	
PERF_ENTER_GR:
	LD	A,0FFH			;
	LD	(GR_MODE),A		; GRAPHICS MODE
	JP	SET_STATE_NORMAL	;	

	
;__PERF_EXIT_GR____________________________________________________________________________________
;
; 	PERFORM EXIT GRAPHICS MODE
;__________________________________________________________________________________________________	
PERF_EXIT_GR:
	LD	A,00FH			;
	LD	(GR_MODE),A		; GRAPHICS MODE
	JP	SET_STATE_NORMAL	;	
	
	
;__PERF_ENTER_ALT___________________________________________________________________________________
;
; 	PERFORM ENTER ALTERNATE KEYPAD MODE
;__________________________________________________________________________________________________	
PERF_ENTER_ALT:
	LD	A,0FFH			;
	LD	(ALT_KEYPAD),A		; ALT KEYPAD
	JP	SET_STATE_NORMAL	;	
	
	
;__PERF_EXIT_ALT___________________________________________________________________________________
;
; 	PERFORM EXIT ALTERNATE KEYPAD MODE
;__________________________________________________________________________________________________	
PERF_EXIT_ALT:
	LD	A,00H			;
	LD	(ALT_KEYPAD),A		; ALT KEYPAD
	JP	SET_STATE_NORMAL	;
	
;__PERF_ENTER_HOLD_________________________________________________________________________________
;
; 	PERFORM ENTER HOLD MODE
;__________________________________________________________________________________________________	
PERF_ENTER_HOLD:
	;  ********* IGNORE HOLD MODE !!
	JP	SET_STATE_NORMAL	;	
	
	
;__PERF_EXIT_HOLD__________________________________________________________________________________
;
; 	PERFORM EXIT HOLD MODE
;__________________________________________________________________________________________________	
PERF_EXIT_HOLD:
	;  ********* IGNORE HOLD MODE !!
	JP	SET_STATE_NORMAL	;	
	
	
;__SET_STATE_NORMAL________________________________________________________________________________
;
; 	SET NORMAL STATE
;__________________________________________________________________________________________________	
SET_STATE_NORMAL:
	LD	A,STATE_NORMAL		; RESET STATE
	LD	(TERMSTATE),A		;
	RET	
	
;__PERF_ERASE_EOL__________________________________________________________________________________
;
; 	PERFORM ERASE FROM CURSOR POS TO END OF LINE
;__________________________________________________________________________________________________	
PERF_ERASE_EOL:	
	LD	A,(TERM_X)		; GET CURRENT CURSOR X COORD
	LD	C,A			; STORE IT IN C
	LD	A,80			; MOVE CURRENT LINE WIDTH INTO A
	SUB	C			; GET REMAINING POSITIONS ON CURRENT LINE
	LD	B,A			; MOVE IT INTO B
	LD 	A, 31		        ; UPDATE TOGGLE VDU CHIP
    	OUT 	(SY6545S),A   		;	
PERF_ERASE_EOL_LOOP:		
	CALL 	VDU_UpdateCheck 	; WAIT FOR VDU CHIP TO BE READY
	LD	A,32			; MOVE SPACE CHARACTER INTO A
	OUT 	(WRITR),A    	     	; WRITE IT TO SCREEN, VDU WILL AUTO INC TO NEXT ADDRESS
	DJNZ    PERF_ERASE_EOL_LOOP	; LOOP UNTIL DONE
	CALL	GOTO_XY			; MOVE CURSOR BACK TO ORIGINAL POSITION
	CALL	SET_STATE_NORMAL	; SET NORMAL STATE
	RET

;__PERF_ERASE_EOS__________________________________________________________________________________
;
; 	PERFORM ERASE FROM CURSOR POS TO END OF SCREEN
;__________________________________________________________________________________________________	
PERF_ERASE_EOS:	
	LD	HL,0780H		; SET SCREEN SIZE INTO HL
	PUSH	HL			; MOVE IT TO DE
	POP	DE			;
	LD 	A, 31		        ; UPDATE TOGGLE VDU CHIP
    	OUT 	(SY6545S),A   		;	
PERF_ERASE_EOS_LOOP:		
    	CALL 	VDU_UpdateCheck		; WAIT FOR VDU CHIP TO BE READY
    	LD 	A, ' '           	; MOVE SPACE CHARACTER INTO A
    	OUT 	(WRITR),A         	; WRITE IT TO SCREEN, VDU WILL AUTO INC TO NEXT ADDRESS
    	DEC	DE			; DEC COUNTER
    	LD 	A,D			; IS COUNTER 0 YET?
    	OR 	E			;
    	JP 	NZ,PERF_ERASE_EOS_LOOP	; NO, LOOP
	CALL	GOTO_XY			; YES, MOVE CURSOR BACK TO ORIGINAL POSITION
	CALL	SET_STATE_NORMAL	; SET NORMAL STATE
	RET

	
;__PERF_IDENTIFY___________________________________________________________________________________
;
; 	PERFORM TERMINAL IDENTIFY FUNCTION
;__________________________________________________________________________________________________	
PERF_IDENTIFY:
	LD	A,ESC_KEY		;
	CALL	KB_ENQUEUE		; STORE ON KB QUEUE
	LD	A,'/'			;
	CALL	KB_ENQUEUE		; STORE ON KB QUEUE
	LD	A,'K'			;
	CALL	KB_ENQUEUE		; STORE ON KB QUEUE
	CALL	SET_STATE_NORMAL	; SET NORMAL STATE
	RET
	
;__PERF_CURSOR_HOME________________________________________________________________________________
;
; 	PERFORM CURSOR HOME
;__________________________________________________________________________________________________	
PERF_CURSOR_HOME:
	LD	A,0			; LOAD 0 INTO A
	LD	(TERM_X),A		; SET X COORD
	LD	(TERM_Y),A		; SET Y COORD
	CALL	SET_STATE_NORMAL	; SET NORMAL STATE
	JP	GOTO_XY			; MOVE CURSOR TO POSITION
		
;__PERF_CURSOR_LEFT________________________________________________________________________________
;
; 	PERFORM CURSOR LEFT
;__________________________________________________________________________________________________	
PERF_CURSOR_LEFT:
	LD	A,(TERM_X)		; GET CURRENT X COORD INTO A
	OR	A			; IS ZERO?
	JP	Z,PERF_CURSOR_ABORT	; YES, ABORT
	DEC	A			; MOVE ONE TO THE LEFT
	LD	(TERM_X),A		; STORE NEW CURSOR POSITION
	CALL	SET_STATE_NORMAL	; SET NORMAL STATE	 
	JP	GOTO_XY			; MOVE CURSOR TO POSITION
	
;__PERF_CURSOR_RIGHT_______________________________________________________________________________
;
; 	PERFORM CURSOR RIGHT
;__________________________________________________________________________________________________	
PERF_CURSOR_RIGHT:
	LD	A,(TERM_X)		; GET CURRENT X COORD INTO A
	CP	79			; IS END OF LINE?
	JP	Z,PERF_CURSOR_ABORT	; YES, ABORT
	INC	A			; MOVE ONE TO THE RIGHT
	LD	(TERM_X),A		; STORE NEW CURSOR POSITION
	CALL	SET_STATE_NORMAL	; SET NORMAL STATE
	JP	GOTO_XY			; MOVE CURSOR TO POSITION
	
;__PERF_CURSOR_UP__________________________________________________________________________________
;
; 	PERFORM CURSOR UP
;__________________________________________________________________________________________________		
PERF_CURSOR_UP:
	LD	A,(TERM_Y)		; GET CURRENT Y COORD INTO A
	OR	A			; IS ZERO?
	JP	Z,PERF_CURSOR_ABORT	; YES, ABORT
	DEC	A			; MOVE UP ONE POSITION
	LD	(TERM_Y),A		; STORE NEW CURSOR POSITION
	CALL	SET_STATE_NORMAL	; SET NORMAL STATE
	JP	GOTO_XY			; MOVE CURSOR TO POSTION
	
	
;__PERF_CURSOR_DOWN________________________________________________________________________________
;
; 	PERFORM CURSOR DOWN
;__________________________________________________________________________________________________		
PERF_CURSOR_DOWN:	
	LD	A,(TERM_Y)		; GET CURRENT Y COORD INTO A
	CP	23			; IS END OF SCREEN?
	JP	Z,PERF_CURSOR_ABORT	; YES, ABORT
	INC	A			; NO, MOVE DOWN ONE POSITION
	LD	(TERM_Y),A		; STORE NEW CURSOR POSITION
PERF_CURSOR_ABORT:
	CALL	SET_STATE_NORMAL	; SET NORMAL STATE
	JP	GOTO_XY			; MOVE CURSOR TO POSITION

	


	
;__DO_SCROLL_______________________________________________________________________________________
;
; 	SCROLL THE SCREEN UP ONE LINE
;__________________________________________________________________________________________________			
DO_SCROLL:
	PUSH	AF			; STORE AF	
DO_SCROLL1:
	PUSH	HL			; STORE HL
	PUSH	BC			; STORE BC
	LD 	A, 31            	; TOGGLE VDU FOR UPDATE
	OUT 	(SY6545S),A         	;
	CALL 	VDU_UpdateCheck 	; WAIT FOR VDU TO BE READY
	LD 	HL, (VDU_DISPLAY_START)	; GET UP START OF DISPLAY
	LD	DE,0050H		; SET AMOUNT TO ADD
	ADD	HL,DE			; ADD TO START POS
	LD	(VDU_DISPLAY_START),HL	; STORE DISPLAY START
	LD 	A, 12			; SAVE START OF DISPLAY TO VDU
	CALL 	VDU_HL2WREG_A		;
    	LD	A,23			; SET CURSOR TO BEGINNING OF LAST LINE
    	LD	(TERM_Y),A		;
    	LD	A,(TERM_X)		;
    	PUSH	AF			; STORE X COORD
    	LD	A,0			;
    	LD	(TERM_X),A		;
    	CALL	GOTO_XY			; SET CURSOR POSITION TO BEGINNING OF LINE
    	CALL	PERF_ERASE_EOL		; ERASE SCROLLED LINE
	POP	AF			; RESTORE X COORD
	LD	(TERM_X),A		;
    	CALL	GOTO_XY			; SET CURSOR POSITION
    	POP	BC			; RESTORE BC
    	POP	HL			; RESTORE HL
    	POP	AF			; RESTORE AF
    	RET				;
    	
;__REVERSE_SCROLL__________________________________________________________________________________
;
; 	SCROLL THE SCREEN DOWN ONE LINE
;__________________________________________________________________________________________________			
REVERSE_SCROLL:
	PUSH	AF			; STORE AF
	PUSH	HL			; STORE HL
	PUSH	BC			; STORE BC
	LD 	A, 31            	; TOGGLE VDU FOR UPDATE
	OUT 	(SY6545S),A         	;
	CALL 	VDU_UpdateCheck 	; WAIT FOR VDU TO BE READY
	LD 	HL, (VDU_DISPLAY_START)	; GET UP START OF DISPLAY
	LD	DE,0FFB0H		; SET AMOUNT TO SUBTRACT (TWOS COMPLEMENT 50H)
	ADD	HL,DE			; ADD TO START POS
	LD	(VDU_DISPLAY_START),HL	; STORE DISPLAY START
	LD 	A, 12			; SAVE START OF DISPLAY TO VDU
	CALL 	VDU_HL2WREG_A		;
    	LD	A,23			; SET CURSOR TO BEGINNING OF LAST LINE
    	LD	(TERM_Y),A		;
    	LD	A,(TERM_X)		;
    	PUSH	AF			; STORE X COORD
    	LD	A,0			;
    	LD	(TERM_X),A		;
    	CALL	GOTO_XY			; SET CURSOR POSITION TO BEGINNING OF LINE
    	CALL	PERF_ERASE_EOL		; ERASE SCROLLED LINE
	POP	AF			; RESTORE X COORD
	LD	(TERM_X),A		;
    	CALL	GOTO_XY			; SET CURSOR POSITION
    	POP	BC			; RESTORE BC
    	POP	HL			; RESTORE HL
    	POP	AF			; RESTORE AF
    	RET				;
    			
	
	
;__WAIT_KBHIT______________________________________________________________________________________
;
; 	WAIT FOR A KEY PRESS
;__________________________________________________________________________________________________			
WAIT_KBHIT:
	CALL 	KB_PROCESS		; call keyboard routine
	LD	A,(KB_QUEUE_PTR)	; IS QUEUE EMPTY?
	OR 	A			; set flags
	JP 	Z,WAIT_KBHIT		; if no keys waiting, try again
	RET

	
;__IS_KBHIT________________________________________________________________________________________
;
; 	WAS A KEY PRESSED?
;__________________________________________________________________________________________________			
IS_KBHIT:
	CALL 	KB_PROCESS		; call keyboard routine
	LD 	A,(KB_QUEUE_PTR)	; ask if keyboard has key waiting
	RET
	
				
;__GET_KEY_________________________________________________________________________________________
;
; 	GET KEY PRESS VALUE
;__________________________________________________________________________________________________			
GET_KEY:
	CALL	WAIT_KBHIT		; WAIT FOR A KEY
	LD	A,(KB_QUEUE_PTR)	; GET QUEUE POINTER
	OR	A			;
	RET	Z			; ABORT IF QUEUE EMPTY
	PUSH	BC			; STORE BC
	LD	B,A			; STORE QUEUE COUNT FOR LATER
	PUSH	HL			; STORE HL
	LD	A,(KB_QUEUE)		; GET TOP BYTE FROM QUEUE
	PUSH 	AF			; STORE IT
	LD	HL,KB_QUEUE		; GET POINTER TO QUEUE
GET_KEY_LOOP:				;
	INC	HL			; POINT TO NEXT VALUE IN QUEUE
	LD	A,(HL)			; GET VALUE
	DEC 	HL			;
	LD	(HL),A			; MOVE IT UP ONE
	INC	HL			;
	DJNZ	GET_KEY_LOOP		; LOOP UNTIL DONE
	LD	A,(KB_QUEUE_PTR)	; DECREASE QUEUE POINTER BY ONE	
	DEC	A			;
	LD	(KB_QUEUE_PTR),A	;
	POP	AF			; RESTORE VALUE
	POP	HL			; RESTORE HL
	POP	BC			; RESTORE BC
	RET		

		
		
	
	

;__VDU_INIT_________________________________________________________________________________________
;
; 	INITIALIZE VDU
;__________________________________________________________________________________________________			
VDU_INIT:
	PUSH 	AF			; STORE AF
	PUSH 	DE			; STORE DE
	PUSH 	HL			; STORE HL

	CALL 	VDU_CRTInit		; INIT 6545 VDU CHIP	
	LD 	A, 31			; TOGGLE VDU FOR UPDATE
	OUT 	(SY6545S),A		;
	LD	HL,0			; SET-UP START OF DISPLAY 
	LD 	DE, 2048    		; SET-UP DISPLAY SIZE
	LD 	A, 18            	; write HL to R18 and R19 (update address)
	CALL 	VDU_HL2WREG_A  		;
	LD 	A, 31            	; TOGGLE VDU FOR UPDATE
	OUT 	(SY6545S),A         	;
VDU_CRTSpaceLoop:			;
	CALL 	VDU_UpdateCheck 	; WAIT FOR VDU TO BE READY
	LD 	A, ' '           	; CLEAR SCREEN
	OUT 	(WRITR),A         	; send space to dataport
	DEC	DE			; DECREMENT DE
	LD 	A,D			; IS ZERO?
	OR 	E			;
	JP 	NZ, VDU_CRTSpaceLoop	; NO, LOOP
	LD 	A, 31            	; TOGGLE VDU FOR UPDATE
	OUT 	(SY6545S),A         	;
	LD 	HL, 0			; SET UP START OF DISPLAY
	LD	(VDU_DISPLAY_START),HL	; STORE DISPLAY START
	LD 	A, 12			; SAVE START OF DISPLAY TO VDU
	CALL 	VDU_HL2WREG_A		;
	POP 	HL			;
	POP 	DE			;
	POP 	AF			;
	CALL	PERF_CURSOR_HOME	; CURSOR HOME	
	CALL	PERF_ERASE_EOS		; CLEAR SCREEN
    	CALL 	VDU_CursorOn		; TURN ON CURSOR
	RET	
	

;__DSPMATRIX_______________________________________________________________________________________
;
; 	DISPLAY INTRO SCREEN
;__________________________________________________________________________________________________			
DSPMATRIX:
	CALL	PERF_CURSOR_HOME	; RESET CURSOR TO HOME POSITION
    	LD	HL,TESTMATRIX		; SET HL TO SCREEN IMAGE
	LD 	DE, 1918    		; SET IMAGE SIZE
DSPMATRIX_LOOP:    	
    	LD 	A,(HL)			; GET NEXT CHAR FROM IMAGE
    	call 	VDU_PutChar		; DUMP CHAR TO DISPLAY
    	INC	HL			; INC POINTER
	DEC	DE			; DEC COUNTER
    	LD 	A,D			; IS COUNTER ZERO?
    	OR 	E			;
    	JP 	NZ,DSPMATRIX_LOOP	; NO, LOOP
	CALL	PERF_CURSOR_HOME	; YES, RESET CURSOR TO HOME POSITION
	RET

TESTMATRIX:
 .TEXT "0         1         2         3         4         5         6         7         "
 .TEXT "01234567890123456789012345678901234567890123456789012345678901234567890123456789"
 .TEXT "2                                                                               "
 .TEXT "3                                                                               "
 .TEXT "4                 ===============================================               "
 .TEXT "5                                                                               "
 .TEXT "6                   *     *  ****    *    *        ****   *  *                  "
 .TEXT "7                   *     *  *   *   *    *       *    *  * *                   "
 .TEXT "8                   *     *  *    *  *    *       *    *  **                    "
 .TEXT "9                    *   *   *    *  *    *       *    *  **                    "
 .TEXT "10                    * *    *   *   *    *       *    *  * *                   "
 .TEXT "11                     *     ****     ****         ****   *  *                  "
 .TEXT "12                                                                              "
 .TEXT "13                ===============================================               "
 .TEXT "14                                                                              "
 .TEXT "15                         VDU TEST V0.1   VT-52 EMULATION                      "
 .TEXT "16                                                                              "
 .TEXT "17                  **  PRESS ANY KEY TO ENTER TERMINAL MODE **                 "
 .TEXT "18                                                                              "
 .TEXT "19                                                                              "
 .TEXT "21                                                                              "
 .TEXT "22                                                                              "
 .TEXT "23                                                                              "
 .TEXT "24                                                                              "
 .TEXT "25                                                                              "


;__VDU_HL2WREG_A___________________________________________________________________________________
;
; 	WRITE VALUE IN HL TO REGISTER IN A
;	A: REGISTER TO UPDATE
;	HL: WORD VALUE TO WRITE
;__________________________________________________________________________________________________			
VDU_HL2WREG_A:
	PUSH 	BC			; STORE BC
	PUSH 	AF			; STORE AF
    	CALL 	VDU_UpdateCheck 	; WAIT FOR VDU TO BE READY
    	POP 	AF			; RESTORE AF
    	LD 	C, SY6545S           	; address register
    	OUT 	(C), A          	; select register (A)
    	INC 	C               	; next write in register
    	OUT 	(C), H          	; write H to selected register
    	DEC 	C               	; next write select register
    	INC 	A               	; increase register number
    	OUT 	(C), A          	; select register (A+1)
    	INC 	C              		; next write in register
    	OUT 	(C), L          	; write L to selected register
    	POP 	BC			; RESTORE BC
    	RET

;__VDU_UpdateCheck_________________________________________________________________________________
;
; 	WAIT FOR VDU TO BE READY
;__________________________________________________________________________________________________			
VDU_UpdateCheck:
    	IN 	A,(SY6545S)          	; read address/status register
    	BIT 	7,A             	; if bit 7 = 1 than an update strobe has been occured
    	RET 	NZ			;
    	JR 	VDU_UpdateCheck  	; wait for ready

VDU_Init6845:
;    .DB  $7F, $50, $60, $4C, $19, $1F, $19, $1A, $78, $09, $60, $09, $00, $00, $00, $00
    .DB	 $7F, $50, $60, $0C, $1E, $02, $18, $1C, $78, $09, $60, $09, $00, $00, $00, $00
;__VDU_CRTInit_____________________________________________________________________________________
;
; 	INIT VDU CHIP
;__________________________________________________________________________________________________			   	
VDU_CRTInit:
    	PUSH 	AF			; STORE AF
    	PUSH 	BC			; STORE BC
    	PUSH 	DE			; STORE DE
    	PUSH 	HL			; STORE HL
    	LD 	BC,$10F2         	; B = 16, C = SY6545S
    	LD 	HL,VDU_Init6845  	; HL = pointer to the default values
    	XOR 	A               	; A = 0
VDU_CRTInitLoop:
    	OUT 	(C), A          	; SY6545S set register
    	INC 	C               	; $F3
    	LD 	D,(HL)          	; load the next default value in D
    	OUT 	(C),D          		; $F3 address
    	DEC 	C               	; SY6545S
    	INC 	HL              	; tab + 1
    	INC 	A               	; reg + 1
    	DJNZ 	VDU_CRTInitLoop		; LOOP UNTIL DONE
    	POP 	HL			; RESTORE HL
    	POP 	DE			; RESTORE DE
    	POP	BC			; RESTORE BC
    	POP	AF			; RESTORE AF
    	RET


;__VDU_CursorOn____________________________________________________________________________________
;
; 	TURN ON CURSOR
;__________________________________________________________________________________________________			   	
VDU_CursorOn:
    	PUSH 	AF			; STORE AF
    	LD 	A, $60			; SET CURSOR VALUE
    	JP 	VDU_CursorSet		;

;__VDU_CursorOff___________________________________________________________________________________
;
; 	TURN OFF CURSOR
;__________________________________________________________________________________________________			   	   	
VDU_CursorOff:
    	PUSH 	AF			; STORE AF
    	LD 	A, $20			; SET CURSOR VALUE
VDU_CursorSet:
	PUSH 	BC			; STORE BC
    	LD 	C,A			; MOVE A TO C
    	CALL 	VDU_UpdateCheck    	; WAIT FOR VDU TO BE READY
    	LD 	A, 10            	; R10, cursor start and status
    	OUT 	(SY6545S), A		; 
    	LD 	A,C			; STORE CURSOR VALUE
    	OUT 	(SY6545D), A        	;
    	POP 	BC			; RESTORE BC
    	POP 	AF			; RESTORE AF
    	RET

;__GOTO_XY_________________________________________________________________________________________
;
; 	MOVE CURSOR TO POSITON IN TERM_X AND TERM_Y
;__________________________________________________________________________________________________			
GOTO_XY:
	PUSH	AF			; STORE AF

	LD	A,(TERM_Y)		; PLACE Y COORD IN A
	CP	24			; IS 24?
	JP	Z,DO_SCROLL1		; YES, MUST SCROLL

    	PUSH 	BC			; STORE BC
    	PUSH 	DE			; STORE DE
	LD	A,(TERM_X)		;
	LD	H,A			;
    	LD	A,(TERM_Y)		;
    	LD	L,A			;    	
    	PUSH 	HL			; STORE HL
    	LD 	B, A             	; B = Y COORD
    	LD 	DE, 80			; MOVE LINE LENGTH INTO DE
    	LD 	HL, 0			; MOVE 0 INTO HL
    	LD 	A, B             	; A=B
    	CP 	0			; Y=0?
    	JP 	Z, VDU_YLoopEnd  	; THEN DO NOT MULTIPLY BY 80
VDU_YLoop:              		; HL = 80 * Y
    	ADD 	HL, DE			; HL=HL+DE
    	DJNZ 	VDU_YLoop		; LOOP 
VDU_YLoopEnd:				;
    	POP 	DE              	; DE = org HL
    	LD 	E, D             	; E = X
    	LD 	D, 0             	; D = 0
    	ADD 	HL, DE          	; HL = HL + X
    	LD 	(VDU_DisplayPos), HL	;
	PUSH	HL			;
	POP	DE			;
	LD	HL,(VDU_DISPLAY_START)	;
	ADD	HL,DE			;    	
    	LD 	A, 18			; SET UPDATE ADDRESS IN VDU
    	CALL 	VDU_HL2WREG_A		;
    	LD 	A, 31            	; TOGGLE VDU FOR UPDATE
    	OUT 	(SY6545S),A         	;
    	LD 	A, 14            	; set cursor pos
    	CALL 	VDU_HL2WREG_A		;
    	POP 	DE			; RESTORE DE
   	POP 	BC			; RESTORE BC
    	POP 	AF			; RESTORE AF
    	RET

;__VDU_PutChar______________________________________________________________________________________
;
; 	PLACE CHARACTER ON SCREEN
;	A: CHARACTER TO OUTPUT
;__________________________________________________________________________________________________			   	   	
VDU_PutChar:
	PUSH	DE			; STORE DE
    	PUSH 	AF			; STORE AF
    	LD	A,(TERM_X)		; PLACE X COORD IN A
    	INC	A			; INC X COORD
    	LD	(TERM_X),A		; STORE IN A
    	CP	80			; IS 80?
    	JP	NZ,VDU_PutChar1		; NO, PLACE CHAR ON DISPLAY
    	LD  	A,0			; YES, WRAP TO NEXT LINE
    	LD	(TERM_X),A		; STORE X
    	LD	A,(TERM_Y)		; A= Y COORD
    	INC 	A			; INC Y COORD
    	LD	(TERM_Y),A		; STORE Y
    	CP	24			; IS PAST END OF SCREEN?
    	CALL 	Z,GOTO_XY        	; YES, HANDLE SCROLLING
VDU_PutChar1:				;
    	CALL 	VDU_UpdateCheck		; WAIT FOR VDU TO BE READY
    					;
    	LD 	A, 31            	; TOGGLE VDU FOR UPDATE
    	OUT 	(SY6545S),A         	;
					;
    	CALL 	VDU_UpdateCheck		; WAIT FOR VDU TO BE READY
					;
    	LD 	A, 31           	; TOGGLE VDU FOR UPDATE
    	OUT 	(SY6545S),A         	;
					;
    	POP 	AF			; RESTORE CHAR
    	OUT 	(WRITR), A		; OUTPUT CHAR TO VDU
    	PUSH 	AF			; STORE AF
    	PUSH 	HL			; STORE HL
    	LD 	HL, (VDU_DisplayPos)	; GET CURRENT DISPLAY ADDRESS
    	INC 	HL			; INCREMENT IT
    	LD 	(VDU_DisplayPos), HL	; STORE CURRENT DISPLAY ADDRESS
    	PUSH	HL			; MOVE HL TO DE
    	POP	DE			;
    	LD	HL,(VDU_DISPLAY_START)	;
    	ADD	HL,DE			;
    	LD 	A, 14			; UPDATE CURSOR POSITION IN HARDWARE
    	CALL 	VDU_HL2WREG_A		;
    	POP 	HL			; RESTORE HL
    	POP 	AF			; RESTORE AF
    	POP	DE			; RESTORE DE
    	RET

    	

    	
	

;__PR_OUTCHAR______________________________________________________________________________________
;
; 	PR_OUTCHAR- OUTPUT CHAR TO PRINTER PORT
;	A: CHAR TO OUTPUT
;__________________________________________________________________________________________________			   	   	
PR_OUTCHAR:
	PUSH	AF			; STORE AF
PR_OUTCHAR_LOOP:
	IN	A,(ppib)		; GET STATUS INFO	
	AND	10000000B		; ONLY INTERESTED IN BUSY FLAG
	JP	NZ,PR_OUTCHAR_LOOP	; LOOP IF BUSY
	POP	AF			; RESTORE AF
	OUT	(ppia),A		; OUTPUT DATA TO PORT
	ld 	a,1			; .01 second delay 
	call 	KB_delay		; ignore anything back after a reset
	CALL	KB_portcbit0Low		; STROBE
	ld 	a,1			; .01 second delay 
	call 	KB_delay		; ignore anything back after a reset
	CALL	KB_portcbit0High	; STROBE
	ret

;__PR_INITIALIZE___________________________________________________________________________________
;
; 	initialise - SET UP PORT FOR PRINTING
;__________________________________________________________________________________________________			   	   	
PR_INITIALIZE:
	CALL	KB_portcbit0High	; STROBE
	CALL	KB_portcbit1High	; FORM FEED
	CALL	KB_portcbit2Low		; DEVICE SELECT
	CALL	KB_portcbit3Low		; DEVICE INIT
	ld 	a,200			; 1 second delay 
	call 	KB_delay		; ignore anything back after a reset
	CALL	KB_portcbit3High	; DEVICE INIT
	ret
		

;__KB_INITIALIZE___________________________________________________________________________________
;
; 	initialise - clear some locations and send a reset to the keyboard
;__________________________________________________________________________________________________			   	   	
KB_INITIALIZE:
	call 	KB_SETPORTC		; sets port c so can input and output
	call 	KB_RESET		; reset to the keyboard
	ld 	a,200			; 1 second delay as keyboard sends stuff back when reset
	call 	KB_delay		; ignore anything back after a reset
	LD	A,0			; EMPTY KB QUEUE
	LD	(KB_QUEUE_PTR),A	; 
	ret


;__KB_RESET________________________________________________________________________________________
;
; 	RESET THE KEYBOARD
;__________________________________________________________________________________________________			   	   	
KB_RESET:
	call 	KB_datahigh		;
	call 	KB_clockhigh		;
	ld 	b,255			;
sf1:	djnz 	sf1			;
	call 	KB_clocklow		; step 1
	ld 	b,255			;
sf2:	djnz 	sf2			;
	call 	KB_datalow		; step 2
	call 	KB_clockhigh		; step 3
	call 	KB_waitclocklow		; step 4
	ld	b,9			; 8 data bits + 1 parity bit low
sf3:	push 	bc			;
	call 	KB_datahigh		; step 5
	call 	KB_waitclockhigh	; step 6
	call 	KB_waitclocklow		; step 7
	pop 	bc			;
	djnz 	sf3			;
	call 	KB_datahigh		; step9
	call 	KB_waitclocklow		; step 10 could read the ack bit here if want to
	call 	KB_waitclockhigh	; step 11
	ld 	b,255			;
sf4:	djnz 	sf4			; finish up delay
	ret

;__KB_SETPORTC_____________________________________________________________________________________
;
; 	SETUP PORT C OF 8255 FOR KEYBOARD
;__________________________________________________________________________________________________			   	   	
KB_SETPORTC:
	ld 	a,10000010B		; a=out b=IN, c high=OUT, clow=out
	out 	(ppicont),a		; PPI control port
	ld 	a,00000000B		; port A to zero as need this for comms to work
	out	(ppia),a		; PPI port A
	call 	KB_datahigh		;
	call 	KB_clockhigh		;
	ld 	a,0			;
	ld 	(capslock),a		; set capslock off to start
	ld 	(ctrl),a		; control off
	ld 	(numlock),a		; numlock off
	ret
;_________________________________________________________________________________________________
;
; 	port C bit routines
;__________________________________________________________________________________________________			   	   	
KB_portcbit0High:			;
	ld 	a,01110001B		; see the 8255 data sheet
	JP	KB_SETBITS		;
KB_portcbit1High:			;
	ld 	a,01110011B		; see the 8255 data sheet
	JP	KB_SETBITS		;
KB_portcbit2High:			;
	ld 	a,01110101B		; see the 8255 data sheet
	JP	KB_SETBITS		;
KB_portcbit3High:			;
	ld 	a,01110111B		; see the 8255 data sheet
	JP	KB_SETBITS		;
KB_portcbit0Low:			;
	ld 	a,01110000B		; see the 8255 data sheet
	JP	KB_SETBITS		;
KB_portcbit1Low:			;
	ld 	a,01110010B		; see the 8255 data sheet
	JP	KB_SETBITS		;
KB_portcbit2Low:			;
	ld 	a,01110100B		; see the 8255 data sheet
	JP	KB_SETBITS		;
KB_portcbit3Low:			;
	ld 	a,01110110B		; see the 8255 data sheet
	JP	KB_SETBITS		;
KB_datahigh:
KB_portcbit4high:			;
	ld 	a,01111001B		; see the 8255 data sheet
	JP	KB_SETBITS		;
KB_datalow:				;
KB_portcbit4low:			;
	ld 	a,01111000B		; see the 8255 data sheet
	JP	KB_SETBITS		;
KB_clockhigh:				;
KB_portcbit5high:			;
	ld 	a,01111011B		; bit 5 high
	JP	KB_SETBITS		;
KB_clocklow:				;
portcbit5low:				;
	ld 	a,01111010B		;
KB_SETBITS:				;
	out 	(ppicont),a		;
	ret				;



;__KB_waitclocklow_________________________________________________________________________________
;
; waitclocklow samples data bit 0, and waits till
; it goes low, then returns
; also times out after 0.001 seconds
; uses a, changes b
;__________________________________________________________________________________________________			   	   	
KB_waitclocklow:
	ld 	b,255		; for timeout counter
wl1:	in 	a,(ppib)	; get a byte from port b
	bit 	1,a		; test the clock bit
	ret 	z		; exit if it went low
	djnz 	wl1		; loop b times
	ret

	
;__KB_waitclockhigh_________________________________________________________________________________
;
; waitclockhigh samples data bit 0, and waits till
; it goes high, then returns
; also times out after 0.001 seconds
; uses a, changes b
;__________________________________________________________________________________________________			   	   	
KB_waitclockhigh:	
	ld 	b,255		; for timeout counter
wh1:	in 	a,(ppib)	; get a byte from port b
	bit 	1,a		; test the clock bit
	ret 	nz		; exit if it went high
	djnz 	wh1		; loop b times
	ret

;__KB_delay________________________________________________________________________________________
;
; pass A - delay is B*0.005 seconds, BCDEHL all preserved
;__________________________________________________________________________________________________			   	   	
KB_delay:	
	push 	bc		; store all variables
	push 	de		;
	push 	hl		;
	ld 	b,a		; put the variable delay in B
	ld 	de,1		;
loop1:	ld 	hl,740		; adjust this value for your clock 1481=3.68Mhz, 3219=8Mhz (test with A=1000=10 secs)
loop2:	sbc 	hl,de		; hl-1
	jr 	nz,loop2	;
	djnz 	loop1		;
	pop 	hl		; restore variables
	pop 	de		;
	pop 	bc		;
	ret			;


;__KB_PROCESS______________________________________________________________________________________
;
;  a=0 if want to know if a byte is available, and a=1 to ask for the byte
;__________________________________________________________________________________________________			   	   	
KB_PROCESS:	
	call	skip		; don't test every one as takes time
	OR	A		; is it zero
	ret	z		; return if zero
 	call	KB_waitbyte	; test keyboard. times out after a bit
	call 	KB_decodechar	; returns char or 0 for things like keyup, some return directly to cp/m
	ret			; return to cp/m

	
;-----------------------------------------------
; cpm calls the keyboard quite frequently. If a keyboard was like a uart which can be checked
; with one instruction, that would be fine. But checking a keyboard involves putting the clock line low
; then waiting some time for a possible reply, then reading in bits with timeouts and then returning
; this slows down a lot of cp/m processes, eg try TYPE MYPROG and printing out text
skip:
	ld	b,0		;
	ld 	a,(skipcount)	;
	dec	a		; subtract 1
	ld	(skipcount),a	; store it back
	cp 	0		;
	jp	nz,sk1		; wordstar is very slow even tried a value of 5 to 200 here
	ld	a,200		; only act on every n calls - bigger=better because this sub is quicker than readbits
	ld	(skipcount),a	; reset counter
	ld 	b,1		; flag to say reset counter
sk1:				;
	ld	a,b		; return the value in a
	ret

	
;__KB_decodechar____________________________________________________________________________________
;
; decode character pass a and prints out the char
; on the LCD screen
;__________________________________________________________________________________________________			   	   	
KB_decodechar:
	cp	0		; is it zero
	ret	z		; return if a zero - no need to do anything
	cp 	0F0h		; is a key up (need to do special code for shift)
	jp 	z,deckeyup	; ignore char up
	cp	0E0h		; two byte keypresses
	jp 	z,twobyte	;
	cp 	058h		; caps lock so toggle
	jp 	z,capstog	;
	cp	12h		; shift (down, because up would be trapped by 0F above)
	jp 	z,shiftdown	;
	cp	59h		; other shift key
	jp 	z,shiftdown	;
	cp	014h		; control key
	jp 	z,controldown	;
	cp	05Ah		; enter key
	jp	z,return	;
	cp	066h		; backspace key
	jp	z,backspace	;
	cp	0dh		; tab key
	jp 	z,tabkey	;
	cp	076h		; escape key
	jp	z,escape	;
	ld 	c,a		;
	ld 	b,0		; add bc to hl
	ld 	hl,normalkeys	; offset to add
	add 	hl,bc		;
	jp	testcontrol	; is the control key down?
dc1:	ld 	a,(capslock)	;
	cp 	0		; is it 0, if so then don't add the caps offset
	jr 	z,dc2		;
	ld 	c,080h		; add another 50h to smalls to get caps
	add 	hl,bc		;
dc2:	ld 	a,(hl)		;
	CALL	KB_ENQUEUE	; STORE ON KB QUEUE
	ret			;
testcontrol:
	ld 	a,(ctrl)	;
	cp 	0		; is control being held down?
	jp	z,dc1		; no so go back to test caps lock on
	ld	a,(hl)		; get the letter, should be smalls 
	sub	96		; a=97 so subtract 96 a=1=^A
	CALL	KB_ENQUEUE	; STORE ON KB QUEUE
	ret			; return instead of the ret after dc2
tabkey:				;
	ld 	a,9		;
	CALL	KB_ENQUEUE	; STORE ON KB QUEUE
	ret			;tab
backspace:			;
	ld	a,8		; backspace
	CALL	KB_ENQUEUE	; STORE ON KB QUEUE
	ret			;
escape:				;
	ld	a,27		;
	CALL	KB_ENQUEUE	; STORE ON KB QUEUE
	ret			;
return:				;
	ld	a,13		; carriage return
	CALL	KB_ENQUEUE	; STORE ON KB QUEUE
	ret			;
deckeyup:
	call KB_waitbyte	; ignore key up throw away the character unless a shift 
	cp	012h		; is it a shift
	jp 	z,shiftup	;
	cp	59h		; other shift key
	jp	z,shiftup	;
	cp	014h		; control up
	jp 	z,controlup	; control up
	ld	a,0		; nothing captured so send back a zero 
	ret
twobyte:; already got EO so get the next character
	call 	KB_waitbyte
	cp	0F0h		; see the notes - keyup for E0 keys is Eo F0 nn not F0 Eo!!
	jp	z,twobyteup	;
	cp	071h		; delete
	jp	z,deletekey	;
	cp	05ah		; return on number pad
	jp	z,returnkey	;
	cp	072h		;
	jp	z,downarrow	;
	cp	074h		;
	jp	z,rightarrow	;
	cp	06bh		;
	jp	z,leftarrow	;
	cp	075h		;
	jp	z,uparrow	;
	cp	070h		;
	jp	z,insert	;
	cp	07dh		;
	jp	z,pageup	;
	cp	07ah		;
	jp	z,pagedown	;
	cp	06ch		;
	jp	z,home		;
	cp	069h		;
	jp	z,end		;
	ld 	a,0		; returns nothing
	ret
twobyteup:			;expect a byte and ignore it
	call	KB_waitbyte	;
	ld	a,0		;
	ret			;
home:				;
	ld	a,1BH		; ESC
	CALL	KB_ENQUEUE	; STORE ON KB QUEUE
	ld	a,'?'		; ?
	CALL	KB_ENQUEUE	; STORE ON KB QUEUE
	ld	a,'w'		; w
	CALL	KB_ENQUEUE	; STORE ON KB QUEUE
	ret			;
end:				;
	ld	a,1BH		; ESC
	CALL	KB_ENQUEUE	; STORE ON KB QUEUE
	ld	a,'?'		; ?
	CALL	KB_ENQUEUE	; STORE ON KB QUEUE
	ld	a,'q'		; q
	CALL	KB_ENQUEUE	; STORE ON KB QUEUE
	ret			;
downarrow:			;
	ld	a,1BH		; ESC
	CALL	KB_ENQUEUE	; STORE ON KB QUEUE
	ld	a,'B'		; B
	CALL	KB_ENQUEUE	; STORE ON KB QUEUE
	ret			;
rightarrow:			;
	ld	a,1BH		; ESC
	CALL	KB_ENQUEUE	; STORE ON KB QUEUE
	ld	a,'C'		; C
	CALL	KB_ENQUEUE	; STORE ON KB QUEUE
	ret			;
leftarrow:			;
	ld	a,1BH		; ESC
	CALL	KB_ENQUEUE	; STORE ON KB QUEUE
	ld	a,'D'		; D
	CALL	KB_ENQUEUE	; STORE ON KB QUEUE
	ret			;
uparrow:			;
	ld	a,1BH		; ESC
	CALL	KB_ENQUEUE	; STORE ON KB QUEUE
	ld 	a,'A'		; A
	CALL	KB_ENQUEUE	; STORE ON KB QUEUE
	ret			;	
insert:				;
	ld	a,1BH		; ESC
	CALL	KB_ENQUEUE	; STORE ON KB QUEUE
	ld	a,'?'		; ?
	CALL	KB_ENQUEUE	; STORE ON KB QUEUE
	ld	a,'p'		; p
	CALL	KB_ENQUEUE	; STORE ON KB QUEUE
	ret			;
pageup:				;
	ld	a,1BH		; ESC
	CALL	KB_ENQUEUE	; STORE ON KB QUEUE
	ld	a,'?'		; ?
	CALL	KB_ENQUEUE	; STORE ON KB QUEUE
	ld	a,'y'		; y
	CALL	KB_ENQUEUE	; STORE ON KB QUEUE
	ret			;
pagedown:			;
	ld	a,1BH		; ESC
	CALL	KB_ENQUEUE	; STORE ON KB QUEUE
	ld	a,'?'		; ?
	CALL	KB_ENQUEUE	; STORE ON KB QUEUE
	ld	a,'s'		; s
	CALL	KB_ENQUEUE	; STORE ON KB QUEUE
	ret			;
controldown:			; same code as shiftdown but diff location
	ld 	a,0ffh		;
	ld	(ctrl),a	; control down
	ld	a,0		;
	ret			;
controlup:			; control key up see shift for explanation
	ld	a,0		;
	ld 	(ctrl),a	;
	ld 	a,0		;
	ret			;
returnkey:			;
	ld 	a,13		;
	CALL	KB_ENQUEUE	; STORE ON KB QUEUE
	ret			;
deletekey:			;
	ld 	a,07fh		; delete key value that cp/m uses
	CALL	KB_ENQUEUE	; STORE ON KB QUEUE
	ret			;
capstog:			;
  	ld 	a,(capslock)	;
	xor 	11111111B	; swap all the bits
	ld 	(capslock),a	;
	ld	a,0		; returns nothing
	ret			;
shiftdown:			; shift is special - hold it down and it autorepeats
				; so once it is down, turn caps on and ignore all further shifts
				; only an F0+shift turns caps lock off again
	ld 	a,0ffh		;
	ld 	(capslock),a	;
	ld 	a,0		; returns nothing
	ret			;
shiftup:			; shiftup turns off caps lock definitely
	ld 	a,0		;
	ld 	(capslock),a	;
	ld 	a,0		; returns nothing
	ret			;

;__KB_ENQUEUE______________________________________________________________________________________
;
;  STORE A BYTE IN THE KEYBOARD QUEUE 
;  A: BYTE TO ENQUEUE
;__________________________________________________________________________________________________			   	   		
KB_ENQUEUE:
	PUSH	DE		; STORE DE
	PUSH	HL		; STORE HL	
	PUSH	AF		; STORE VALUE
	LD	A,(KB_QUEUE_PTR); PUT QUEUE POINTER IN A
	CP	15		; IS QUEUE FULL
	JP	P,KB_ENQUEUE_AB	; YES, ABORT	
	LD	HL,KB_QUEUE	; GET QUEUE POINTER
	PUSH	HL		; MOVE HL TO BC
	POP	BC		; 
	LD	H,0		; ZERO OUT H
	LD	L,A		; PLACE QUEUE POINTER IN L
	ADD	HL,BC		; POINT HL AT THE NEXT LOACTION TO ADD VALUE
	POP	AF		; RESTORE VALUE
	LD	(HL),A		; ENQUEUE VALUE
	LD	A,(KB_QUEUE_PTR); GET QUEUE POINTER
	INC	A		; INC IT
	LD	(KB_QUEUE_PTR),A; STORE QUEUE POINTER
KB_ENQUEUE_AB:
	POP	HL		; RESTORE HL
	POP	DE		; RESTORE DE
	RET
	
	
;__KB_waitbyte_____________________________________________________________________________________
;
; wait for a byte - tests a number of times if there is a keyboard input,
; overwrites all registers, returns byte in a
;__________________________________________________________________________________________________			   	   		
KB_waitbyte:	
	call	KB_clockhigh	; turn on keyboard
	ld 	hl,500		; number of times to check 200=slow TYPE , 10=error, 25 ?error 50 ok - this delay has to be there otherwise weird keyup errors
wb1:	push 	hl		; store counter
	call 	KB_readbits	; test for a low on the clock line
	pop 	hl		; get the counter back
	cp	0		; test for a zero back from readbits
	jr	nz,wb2		; if not a zero then must have a byte ie a keyboard press
	ld	de,1		; load with 1
	sbc 	hl,de		; subtract 1
	jr	nz,wb1		; loop waiting for a response
wb2:	push 	af		; store the value in a
	call 	KB_clocklow	; turn off keyboard
	pop af			; get back byte as clocklow erased it
	ret

;__KB_readbits_____________________________________________________________________________________
;
; readbits reads 11 bits in from the keyboard
; first bit is a start bit then 8 bits for the byte
; then a parity bit and a stop bit
; returns after one machine cycle if not low
; uses a, b,d, e 
; returns a=0 if no data, a= scancode (or part thereof)
;__________________________________________________________________________________________________			   	   		
KB_readbits:
	in 	a,(ppib)
	bit 	1,a		; test the clock bit
	jr 	z,r1		; if low then start the capture
	ld 	a,0		; returns a=0 if nothing
	ret			;
r1:	call 	KB_waitclockhigh; if gets to here then must be low so wait till high
	ld 	b,8		; sample 8 times
	ld 	e,0		; start with e=0
r2:	ld 	d,b		; store because waitclockhigh destroys
	call 	KB_waitclocklow	; wait till clock goes low
	in 	a,(ppib)	; sample the data line
	rra			; move the data bit into the carry register
	ld 	a,e		; get the byte we are building in e
	rra			; move the carry bit into bit 7 and shift right
	ld 	e,a		; store it back. After 8 cycles 1st bit read will be in b0
	call 	KB_waitclockhigh; wait till goes high
	ld 	b,d		; restore for loop
	djnz 	r2		; do this 8 times
	call 	KB_waitclocklow	; get the parity bit
	call 	KB_waitclockhigh;
	call 	KB_waitclocklow	; get the stop bit
	call 	KB_waitclockhigh;	
	ld 	a,e		; returns with answer in a
	ret
	
	
normalkeys: ; The TI character codes, offset from label by keyboard scan code
		.db 000,000,000,000,000,000,000,000,000,000
		.db 000,000,000,009,"`",000,000,000,000,000	; 0D = tabkey=9
		.db 000,"q","1",000,000,000,"z","s","a","w"
		.db "2",000,000,"c","x","d","e","4","3",000
		.db 000," ","v","f","t","r","5",000,000,"n"
		.db "b","h","g","y","6",000,000,000,"m","j"
		.db "u","7","8",000,000,",","k","i","o","0"
		.db "9",000,000,".","/","l",";","p","-",000
		.DB 000,000,039,000,"[","=",000,000,000,000	; 39 is '
		.db 000,"]",000,092,000,000,000,000,000,000	; 92 is \
		.db 000,000,000,000,000,"1",000,"4","7",000
		.db 000,000,"0",".","2","5","6","8",000,000
		.db 000,"+","3","-","*","9",000,000		; pad to 80h bytes
		.db 000,000,000,000,000,000,000,000,000,000
		.db 000,000,000,009,"~",000,000,000,000,000	; 0D = tabkey=9
		.db 000,"Q","!",000,000,000,"Z","S","A","W"
		.db "@",000,000,"C","X","D","E","$","#",000
		.db 000," ","V","F","T","R","%",000,000,"N"
		.db "B","H","G","Y","^",000,000,000,"M","J"
		.db "U","&","*",000,000,"<","K","I","O",")"
		.db "(",000,000,">","?","L",":","P","_",000
		.DB 000,000,034,000,"{","+",000,000,000,000	; 34 is "
		.db 000,"}",000,"|",000,000,000,000,000,000	; 92 is \
		.db 000,000,000,000,000,"1",000,"4","7",000
		.db 000,000,"0",".","2","5","6","8",000,000
		.db 000,"+","3","-","*","9",000,000		; pad to 80h bytes



;__________________________________________________________________________________________________
;
; 	RAM STORAGE AREAS
;__________________________________________________________________________________________________			

ALT_KEYPAD	.DB	0		; ALT KEYPAD ENABLED?	
GR_MODE		.DB	0		; GRAPHICS MODE ENABLED?
TERM_X:		.DB	0		; CURSOR X
TERM_Y:		.DB	0		; CURSOR Y
TERMSTATE:	.DB	0		; TERMINAL STATE
					; 0 = NORMAL
					; 1 = ESC RCVD
VDU_DisplayPos:	.DW  	0		; CURRENT DISPLAY POSITION
VDU_DISPLAY_START:
		.DW  	0		; CURRENT DISPLAY POSITION
capslock	.DB  	0		; location for caps lock, either 00000000 or 11111111
ctrl		.DB  	0		; location for ctrl on or off 00000000 or 11111111
numlock		.DB  	0		; location for num lock
skipcount	.DB	0		; only check some calls, speeds up a lot of cp/m

KB_QUEUE	.DB	0,0,0,0,0,0,0,0 ; 16 BYTE KB QUEUE
		.DB	0,0,0,0,0,0,0,0
KB_QUEUE_PTR	.DB	0		; POINTER TO QUEUE			
PARKSTACK	.DW	0000		; SAVE STACK POINTER 

		.DB	0,0,0,0,0,0,0,0 ;
		.DB	0,0,0,0,0,0,0,0	;
		.DB	0,0,0,0,0,0,0,0 ;
		.DB	0,0,0,0,0,0,0,0	;
		.DB	0,0,0,0,0,0,0,0 ;
		.DB	0,0,0,0,0,0,0,0	;
		.DB	0,0,0,0,0,0,0,0 ;
		.DB	0,0,0,0,0,0,0,0	;
		.DB	0,0,0,0,0,0,0,0 ;
		.DB	0,0,0,0,0,0,0,0	;
		.DB	0,0,0,0,0,0,0,0 ;
		.DB	0,0,0,0,0,0,0,0	;
TERMSTACK:	.DB	0		;


	.end
